﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using HomeDump.Core.Contracts;
using NLog;

namespace HomeDump.Server.Components.MusicSearch
{
	/// <summary>
	/// Class responsible for searching a path for music.
	/// </summary>
	public class MusicPath : IMusicPath
	{
		private static readonly Logger Log = LogManager.GetCurrentClassLogger();

		private readonly IMediaFileExtensions mediaExtensions;
		private readonly IDirectoryWrapper directory;
		private readonly IMusicFileParser musicFileParser;


		/// <summary>
		/// Initializes a new instance of the <see cref="MusicPath"/> class.
		/// </summary>
		/// <param name="mediaExtensions">The supported media file extensions.</param>
		/// <param name="directory">Class wrapping the <see cref="Directory"/> class.</param>
		/// <param name="musicFileParser">The music file parser.</param>
		public MusicPath(
			IMediaFileExtensions mediaExtensions,
			IDirectoryWrapper directory,
			IMusicFileParser musicFileParser)
		{
			Requires.IsNotNull(mediaExtensions);
			Requires.IsNotNull(mediaExtensions.Music);
			Requires.IsTrue(mediaExtensions.Music.Any());
			Requires.IsTrue(mediaExtensions.Music.All(musicExtension => !string.IsNullOrEmpty(musicExtension)));
			Requires.IsNotNull(directory);
			Requires.IsNotNull(musicFileParser);

			this.mediaExtensions = mediaExtensions;
			this.directory = directory;
			this.musicFileParser = musicFileParser;
		}


		/// <summary>
		/// Searches specified path for music files asynchronously.
		/// </summary>
		/// <param name="path">The path to search in.</param>
		/// <returns>Task with result containing music files found in specified path.</returns>
		public Task<IEnumerable<MusicFile>> SearchForMusicFilesAsync(string path)
		{
			Requires.IsNotNullOrEmpty(path);

			Log.Info("Search for music files async in '{0}'", path);

			return Task<IEnumerable<MusicFile>>.Factory.StartNew(
				() =>
				{
					IEnumerable<IEnumerable<MusicFile>> musicFilesBasedOnExtension =
						from musicExtension in mediaExtensions.Music
						select SearchForMusicFiles(path, "*" + musicExtension);

					// Flatten sequence of music files
					return musicFilesBasedOnExtension.SelectMany(musicFiles => musicFiles);
				},
				TaskCreationOptions.LongRunning);
		}


		/// <summary>
		/// Searches specified path for music files matching specified search pattern.
		/// </summary>
		/// <param name="path">The path to search in.</param>
		/// <param name="searchPattern">The search pattern.</param>
		/// <returns>
		/// Sequence with music files matching search pattern found in specified path.
		/// </returns>
		private IEnumerable<MusicFile> SearchForMusicFiles(string path, string searchPattern)
		{
			IEnumerable<string> files = directory.EnumerateFiles(
				path,
				searchPattern,
				SearchOption.AllDirectories);

			Log.Info(
				"Found {0} music files with search pattern {1} in {2}",
				files.Count(),
				searchPattern,
				path);

			return
				from file in files
				select musicFileParser.Parse(file);
		}
	}
}